package com.atguigu.tingshu.album.service.impl;

import com.atguigu.tingshu.album.config.VodConstantProperties;
import com.atguigu.tingshu.album.mapper.AlbumInfoMapper;
import com.atguigu.tingshu.album.mapper.TrackInfoMapper;
import com.atguigu.tingshu.album.mapper.TrackStatMapper;
import com.atguigu.tingshu.album.service.TrackInfoService;
import com.atguigu.tingshu.album.service.VodService;
import com.atguigu.tingshu.common.constant.SystemConstant;
import com.atguigu.tingshu.common.result.Result;
import com.atguigu.tingshu.common.util.AuthContextHolder;
import com.atguigu.tingshu.common.util.UploadFileUtil;
import com.atguigu.tingshu.model.album.AlbumInfo;
import com.atguigu.tingshu.model.album.TrackInfo;
import com.atguigu.tingshu.model.album.TrackStat;
import com.atguigu.tingshu.query.album.TrackInfoQuery;
import com.atguigu.tingshu.user.client.UserInfoFeignClient;
import com.atguigu.tingshu.vo.album.AlbumTrackListVo;
import com.atguigu.tingshu.vo.album.TrackInfoVo;
import com.atguigu.tingshu.vo.album.TrackListVo;
import com.atguigu.tingshu.vo.album.TrackMediaInfoVo;
import com.atguigu.tingshu.vo.user.UserInfoVo;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.qcloud.vod.VodUploadClient;
import com.qcloud.vod.model.VodUploadRequest;
import com.qcloud.vod.model.VodUploadResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;
import org.springframework.web.multipart.MultipartFile;
import reactor.netty.udp.UdpServer;

import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;

@Slf4j
@Service
@SuppressWarnings({"all"})
public class TrackInfoServiceImpl extends ServiceImpl<TrackInfoMapper, TrackInfo> implements TrackInfoService {

    @Autowired
    private TrackInfoMapper trackInfoMapper;
    @Autowired
    private VodConstantProperties vodConstantProperties;
    @Autowired
    private AlbumInfoMapper albumInfoMapper;
    @Autowired
    private VodService vodService;
    @Autowired
    private TrackStatMapper trackStatMapper;
    @Autowired
    private UserInfoFeignClient userInfoFeignClient;

    /**
     * 查询用户指定专辑下指定数量的付费声音
     * @param trackId 歌曲ID
     * @param trackCount 查询的歌曲数量
     * @return 返回符合条件的歌曲列表
     */
    @Override
    public List<TrackInfo> findPaidTrackInfoList(Long trackId, Integer trackCount) {
        // 初始化歌曲列表
        List<TrackInfo> list = new ArrayList<>();
        // 根据歌曲ID查询歌曲信息
        TrackInfo trackInfo = this.trackInfoMapper.selectById(trackId);
        // 当查询数量大于0时，执行查询逻辑
        if (trackCount > 0) {
            // 调用远程服务获取用户已付费的歌曲列表
            Result<List<Long>> userPaidTrackListResult = userInfoFeignClient.findUserPaidTrackList(trackInfo.getAlbumId());
            // 断言用户已付费的歌曲列表不为空
            Assert.notNull(userPaidTrackListResult, "查询支付声音服务失败");
            List<Long> userPaidTrackIdList = userPaidTrackListResult.getData();
            Assert.notNull(userPaidTrackIdList, "支付声音为空");
            // 构建查询条件
            LambdaQueryWrapper<TrackInfo> wrapper = new LambdaQueryWrapper<>();
            wrapper.eq(TrackInfo::getAlbumId, trackInfo.getAlbumId());
            wrapper.gt(TrackInfo::getOrderNum, trackInfo.getOrderNum());
            // 当用户已付费的歌曲列表不为空时，排除这些歌曲
            if (!CollectionUtils.isEmpty(userPaidTrackIdList)) {
                wrapper.notIn(TrackInfo::getId, userPaidTrackIdList);
            }
            // 限制查询数量
            wrapper.last("limit " + trackCount);
            // 执行查询并返回结果
            list = trackInfoMapper.selectList(wrapper);
        } else {
            // 当查询数量为0时，直接返回查询到的歌曲信息
            list.add(trackInfo);
        }
        return list;
    }

    /**
     * 查询用户指定声音的付费情况
     *
     * @param trackId 声音ID
     * @return 包含用户付费情况的列表
     */
    @Override
    public List<Map<String, Object>> findUserTrackPaidList(Long trackId) {

        // 根据声音ID查询声音信息
        TrackInfo trackInfo = trackInfoMapper.selectById(trackId);
        // 根据声音所属专辑ID查询专辑信息
        AlbumInfo albumInfo = albumInfoMapper.selectById(trackInfo.getAlbumId());
        // 调用远程服务查询用户对声音的付费列表
        Result<List<Long>> userPaidTrackListResult = userInfoFeignClient.findUserPaidTrackList(trackInfo.getAlbumId());
        // 断言查询结果不为空
        Assert.notNull(userPaidTrackListResult, "查询支付声音服务失败");
        // 获取用户已付费的声音ID列表
        List<Long> paidTrackIdList = userPaidTrackListResult.getData();
        // 断言付费声音列表不为空
        Assert.notNull(paidTrackIdList, "支付声音为空");

        // 构建查询条件，获取同一专辑中顺序号大于当前声音的声音列表
        LambdaQueryWrapper<TrackInfo> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(TrackInfo::getAlbumId, trackInfo.getAlbumId());
        wrapper.gt(TrackInfo::getOrderNum, trackInfo.getOrderNum());

        // 查询符合条件的声音列表
        List<TrackInfo> trackInfoList = trackInfoMapper.selectList(wrapper);
        // 过滤出用户未付费的声音列表
        List<TrackInfo> noPaidTrackList = trackInfoList.stream().filter(trackInfo1 -> !paidTrackIdList.contains(trackInfo1.getId())).collect(Collectors.toList());

        // 初始化最终返回的列表
        List<Map<String, Object>> list = new ArrayList<>();
        // 根据未付费声音的数量，添加不同的付费选项
        if (noPaidTrackList.size() >= 0) {
            HashMap<String, Object> map = new HashMap<>();
            map.put("name", "本集");
            map.put("price", albumInfo.getPrice());
            map.put("trackCount", 0);
            list.add(map);
        }
        if (noPaidTrackList.size() > 0 && noPaidTrackList.size() <= 10) {
            HashMap<String, Object> map = new HashMap<>();
            map.put("name", "剩余" + noPaidTrackList.size() + "集");
            map.put("price", albumInfo.getPrice().multiply(BigDecimal.valueOf(noPaidTrackList.size())));
            map.put("trackCount", noPaidTrackList.size());
            list.add(map);
        }
        if (noPaidTrackList.size() > 10) {
            HashMap<String, Object> map = new HashMap<>();
            map.put("name", "剩余" + noPaidTrackList.size() + "集");
            map.put("price", albumInfo.getPrice().multiply(BigDecimal.valueOf(10)));
            map.put("trackCount", 10);
            list.add(map);
        }
        if (noPaidTrackList.size() > 10 && noPaidTrackList.size() <= 20) {
            HashMap<String, Object> map = new HashMap<>();
            map.put("name", "后" + noPaidTrackList.size() + "集");
            map.put("price", albumInfo.getPrice().multiply(BigDecimal.valueOf(noPaidTrackList.size())));
            map.put("trackCount", noPaidTrackList.size());
            list.add(map);
        }
        if (noPaidTrackList.size() > 20) {
            HashMap<String, Object> map = new HashMap<>();
            map.put("name", "后" + noPaidTrackList.size() + "集");
            map.put("price", albumInfo.getPrice().multiply(BigDecimal.valueOf(20)));
            map.put("trackCount", 20);
            list.add(map);
        }
        if (noPaidTrackList.size() > 20 && noPaidTrackList.size() <= 30) {
            HashMap<String, Object> map = new HashMap<>();
            map.put("name", "后" + noPaidTrackList.size() + "集");
            map.put("price", albumInfo.getPrice().multiply(BigDecimal.valueOf(noPaidTrackList.size())));
            map.put("trackCount", noPaidTrackList.size());
            list.add(map);
        }
        if (noPaidTrackList.size() > 30) {
            HashMap<String, Object> map = new HashMap<>();
            map.put("name", "后" + noPaidTrackList.size() + "集");
            map.put("price", albumInfo.getPrice().multiply(new BigDecimal("30")));
            map.put("trackCount", 30);
            list.add(map);
        }
        if (noPaidTrackList.size() > 30 && noPaidTrackList.size() <= 50) {
            HashMap<String, Object> map = new HashMap<>();
            map.put("name", "后" + noPaidTrackList.size() + "集");
            map.put("price", albumInfo.getPrice().multiply(BigDecimal.valueOf(noPaidTrackList.size())));
            map.put("trackCount", noPaidTrackList.size());
            list.add(map);
        }
        if (noPaidTrackList.size() > 50) {
            HashMap<String, Object> map = new HashMap<>();
            map.put("name", "后" + noPaidTrackList.size() + "集");
            map.put("price", albumInfo.getPrice().multiply(new BigDecimal("50")));
            map.put("trackCount", 50);
            list.add(map);
        }

        // 返回用户付费情况列表
        return list;
    }

    /**
     * 根据专辑id查询声音列表
     *
     * @param page
     * @param albumId
     * @param userId
     * @return
     */

    @Override
    public IPage<AlbumTrackListVo> findAlbumTrackPage(Page<AlbumTrackListVo> page, Long albumId, Long userId) {
        //获取所有声音列表
        IPage<AlbumTrackListVo> albumTrackListVoIPage = trackInfoMapper.selectAlbumTrackPage(page, albumId);
        AlbumInfo albumInfo = albumInfoMapper.selectById(albumId);
        //用户未登录
        if (null == userId) {
            //除试听集数都需要付费
            //获取对象

            //付费专辑
            if (!SystemConstant.ALBUM_PAY_TYPE_FREE.equals(albumInfo.getPayType())) {
                albumTrackListVoIPage.getRecords().stream().filter(albumTrackListVo -> albumTrackListVo.getOrderNum() > albumInfo.getTracksForFree()).forEach(albumTrackListVo -> albumTrackListVo.setIsShowPaidMark(true));


            }
            return albumTrackListVoIPage;
        } else {     //定义一个变量标记是否需要付费
            boolean isNeedPay = false;

            //用户登录后，判断用户的身份
            Result<UserInfoVo> userInfoVoResult = userInfoFeignClient.getUserInfoVo(userId);
            Assert.notNull(userInfoVoResult);
            UserInfoVo userInfoVo = userInfoVoResult.getData();
            Assert.notNull(userInfoVo, "用户信息不存在");
            //判断用户的身份
            if (userInfoVo.getIsVip() == 1 && userInfoVo.getVipExpireTime().before(new Date()) || userInfoVo.getIsVip() == 0) {
                isNeedPay = true;
            } else if (SystemConstant.ALBUM_PAY_TYPE_REQUIRE.equals(albumInfo.getPayType())) {
                isNeedPay = true;
            }
            if (isNeedPay) {
                //获取付费的声音集合
                List<AlbumTrackListVo> albumTrackListVoList = albumTrackListVoIPage.getRecords().stream().filter(albumTrackListVo -> albumTrackListVo.getOrderNum() > albumInfo.getTracksForFree()).toList();
                //获取付费的声音id集合
                List<Long> paidTrackIdList = albumTrackListVoList.stream().map(AlbumTrackListVo::getTrackId).toList();
                Result<Map<Long, Integer>> map = userInfoFeignClient.getUserPaidTrackIdList(userId, paidTrackIdList);
                Assert.notNull(map);
                Map<Long, Integer> paidTrackIdMap = map.getData();
                for (AlbumTrackListVo albumTrackListVo : albumTrackListVoList) {
                    albumTrackListVo.setIsShowPaidMark(paidTrackIdMap.get(albumTrackListVo.getTrackId()) == 0);
                }

            }


        }
        return albumTrackListVoIPage;
    }

    {

    }

    /**
     * 修改声音信息
     * <p>
     * 本方法通过覆盖原有声音文件的方式更新声音信息。首先根据trackId查询数据库中的TrackInfo对象，
     * 然后使用TrackInfoVo对象的属性值覆盖TrackInfo对象的属性值。如果声音文件ID(mediaFileId)为空，
     * 则调用vodService获取和删除媒体文件。最后，更新数据库中的TrackInfo记录。
     *
     * @param trackInfoVo 包含待更新的声音信息的VO对象
     * @param trackId     需要更新的声音的ID
     */
    @Override
    public void updateTrackInfo(TrackInfoVo trackInfoVo, Long trackId) {
        // 根据声音ID查询数据库中的声音信息
        TrackInfo trackInfo = this.trackInfoMapper.selectById(trackId);
        // 将VO对象的属性值复制到实体对象，实现属性覆盖
        BeanUtils.copyProperties(trackInfoVo, trackInfo);
        // 获取声音文件ID
        String mediaFileId = trackInfo.getMediaFileId();
        // 如果声音文件ID为空，则进行以下操作
        if (mediaFileId == null) {
            // 获取媒体文件信息
            vodService.getMediaInfo(mediaFileId);
            // 删除媒体文件
            vodService.removeMedia(mediaFileId);
        }
    }

    /**
     * 根据声音id获取声音信息
     *
     * @param trackId
     * @param trackInfoVo
     * @return
     */
    @Override
    public TrackInfo getTrackInfoById(Long trackId) {
        //返回结果
        return trackInfoMapper.selectById(trackId);
    }


    /**
     * 删除声音信息
     *
     * @param trackId
     */

    @Override
    public void removeTrackInfo(Long trackId) {

        //更新albuminfo表中的includeTrackCount
        TrackInfo trackInfo = this.trackInfoMapper.selectById(trackId);
        //删除trackinfo表
        trackInfoMapper.deleteById(trackId);
        //获取专辑对象
        AlbumInfo albumInfo = albumInfoMapper.selectById(trackInfo.getAlbumId());
        albumInfo.setIncludeTrackCount(albumInfo.getIncludeTrackCount() - 1);
        this.albumInfoMapper.updateById(albumInfo);
        //删除trackstat表中的数据
        trackStatMapper.delete(new LambdaQueryWrapper<TrackStat>().eq(TrackStat::getTrackId, trackId));
        //删除如果是中间值，后面的数据要全变
        this.trackInfoMapper.updateOrderNum(trackInfo.getAlbumId(), trackInfo.getOrderNum());
        //删除云点播
        vodService.removeMedia(trackInfo.getMediaFileId());
    }


    /**
     * 查询用户声音列表
     *
     * @param page1
     * @param trackInfoQuery
     * @return
     */

    @Override
    public IPage<TrackListVo> findUserTrackPage(Page<Object> page1, TrackInfoQuery trackInfoQuery) {
        //返回结果
        return trackInfoMapper.findUserTrackPage(page1, trackInfoQuery);
    }

    /**
     * 保存歌曲信息
     * 将TrackInfoVo对象中的数据保存到TrackInfo表中，并设置相关的歌曲属性，如用户ID、在专辑中的顺序、媒体信息等
     * 此外，还保存歌曲的统计信息，包括播放、收藏、点赞和评论四个维度
     *
     * @param trackInfoVo 包含要保存的歌曲信息的TrackInfoVo对象
     */
    @Override
    public void saveTrackInfo(TrackInfoVo trackInfoVo) {
        // 将TrackInfoVo对象中的数据保存到TrackInfo表中
        TrackInfo trackInfo = new TrackInfo();
        BeanUtils.copyProperties(trackInfoVo, trackInfo);
        // 获取当前用户的ID
        Long userId = AuthContextHolder.getUserId();
        trackInfo.setUserId(userId);

        // 根据专辑ID获取专辑信息，用于设置歌曲在专辑中的顺序
        AlbumInfo albumInfo = this.albumInfoMapper.selectById(trackInfo.getAlbumId());
        trackInfo.setOrderNum(albumInfo.getIncludeTrackCount() + 1);
        // 从云点播服务中获取媒体文件信息
        TrackMediaInfoVo trackMediaInfoVo = vodService.getMediaInfo(trackInfoVo.getMediaFileId());
        // 设置歌曲的媒体播放URL、时长、大小和类型
        trackInfo.setMediaUrl(trackMediaInfoVo.getMediaUrl());
        trackInfo.setMediaDuration(trackMediaInfoVo.getDuration());
        trackInfo.setMediaSize(trackMediaInfoVo.getSize());
        trackInfo.setMediaType(trackMediaInfoVo.getType());
        // 将歌曲信息插入到TrackInfo表中
        trackInfoMapper.insert(trackInfo);
        // 保存歌曲统计信息，包括播放、收藏、点赞和评论四个维度
        TrackStat trackStat = new TrackStat();
        this.saveTrackStat(trackInfo.getId(), SystemConstant.TRACK_STAT_PLAY);
        this.saveTrackStat(trackInfo.getId(), SystemConstant.TRACK_STAT_COLLECT);
        this.saveTrackStat(trackInfo.getId(), SystemConstant.TRACK_STAT_PRAISE);
        this.saveTrackStat(trackInfo.getId(), SystemConstant.TRACK_STAT_COMMENT);
    }

    /**
     * 保存声音统计信息
     *
     * @param id            声音的唯一标识
     * @param trackStatType 统计信息的类型
     *                      <p>
     *                      此方法用于初始化并保存一条轨迹的统计信息，设置轨迹ID和统计类型，
     *                      并将统计数值初始化为0。适用于需要跟踪特定轨迹的统计信息的场景。
     */
    private void saveTrackStat(Long id, String trackStatType) {
        // 创建一个新的声音统计对象
        TrackStat trackStat = new TrackStat();
        // 设置声音ID
        trackStat.setTrackId(id);
        // 设置统计类型
        trackStat.setStatType(trackStatType);
        // 设置统计数值为0
        trackStat.setStatNum(0);
    }

    /**
     * 上传音视频文件方法
     * 该方法用于上传音视频文件到音视频点播服务
     *
     * @param file 需要上传的音视频文件
     * @return 包含上传文件ID和媒体URL的Map对象，上传失败时返回null
     */
    @Override
    public Map<String, Object> uploadTrack(MultipartFile file) {
        // 创建音视频点播上传客户端，使用预置的密钥进行身份验证
        VodUploadClient client = new VodUploadClient(vodConstantProperties.getSecretId(), vodConstantProperties.getSecretKey());

        // 将文件上传到临时路径，并返回临时文件路径
        String tempPath = UploadFileUtil.uploadTempPath(vodConstantProperties.getTempPath(), file);
        // 创建音视频上传请求对象，并设置媒体文件路径
        VodUploadRequest request = new VodUploadRequest();
        request.setMediaFilePath(tempPath);

        // 尝试上传媒体文件到音视频点播服务
        try {
            VodUploadResponse response = client.upload(vodConstantProperties.getRegion(), request);
            // 记录上传文件的ID
            log.info("Upload FileId = {}", response.getFileId());
            // 创建返回结果的HashMap
            HashMap<String, Object> hashMap = new HashMap<>();
            hashMap.put("mediaFileId", response.getFileId());
            hashMap.put("mediaUrl", response.getMediaUrl());
            return hashMap;
        } catch (Exception e) {
            // 业务方进行异常处理
            log.error("Upload Err", e);
        }
        return null;
    }
}
